Authenticating to Java web services with C# using SOAP authentication

Download ProjectWell, we did method 1, basic authentication in our last post: Authenticating to Java web services with C# using basic authentication (using FlexNet services as examples). Let’s do method 2 here.

Method 2 – SOAP Authentication

SOAP authentication is a bit tricky. We have to get to the SOAP request and add headers but we don’t have access to the SOAP header through our SOAP service clients. Once we have access, we need to create custom SoapHeader objects.

We have to use a SoapExtension. A SoapExtension will apply to all SAOP requests, and we may not want that, so we need to enable it based on service.

Step 1 – Create Custom SOAP Header objects for UserId and UserPassword

I created a base object. Notice the XmlText attribute.

using System.Web.Services.Protocols;
using System.Xml.Serialization;

namespace ConnectToFlexeraExample.Model
{
    public class BaseSoapHeader : SoapHeader
    {
        public BaseSoapHeader()
        {
            Actor = "http://schemas.xmlsoap.org/soap/actor/next";
            MustUnderstand = false;
        }

        [XmlText]
        public virtual string Value { get; set; }
    }
}

Then a UserIdSoapHeader object. Notice the XmlRoot attribute.

using System.Xml.Serialization;

namespace ConnectToFlexeraExample.Model
{
    [XmlRoot("UserId", Namespace = "urn:com.macrovision:flexnet/platform")]
    public class UserIdSoapHeader : BaseSoapHeader
    {

    }
}
using System;
using System.Text;
using System.Xml.Serialization;

namespace ConnectToFlexeraExample.Model
{
    [XmlRoot("UserPassword", Namespace = "urn:com.macrovision:flexnet/platform")]
    public class PasswordSoapHeader : BaseSoapHeader
    {
        [XmlIgnore]
        public string EncodedPassword
        {
            set { Value = Convert.ToBase64String(Encoding.UTF8.GetBytes(value)); }
        }
    }
}

Step 2 – Add a SoapHeaderInjectionExtension : SoapExtension

using ConnectToFlexeraExample.Model;
using System;
using System.Collections.Generic;
using System.Net;
using System.Web.Services.Protocols;

namespace ConnectToFlexeraExample.Extensions
{
    public class SoapHeaderInjectionExtension : SoapExtension
    {
        public static Dictionary<string, bool> EnabledServices = new Dictionary<string, bool>();
        public static Dictionary<string, NetworkCredential> UserAndPassword = new Dictionary<string, NetworkCredential>();

        public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
        {
            return null; // Unused
        }

        public override object GetInitializer(Type serviceType)
        {
            return null; // Unused
        }

        public override void Initialize(object initializer)
        {
            // Unused
        }

        public override void ProcessMessage(SoapMessage message)
        {
            if (!IsEnabledForUrl(message.Url))
                return;
            switch (message.Stage)
            {
                case SoapMessageStage.BeforeSerialize:
                    NetworkCredential creds;
                    if (UserAndPassword.TryGetValue(message.Url, out creds))
                    {
                        message.Headers.Add(new UserIdSoapHeader { Value = creds.UserName });
                        message.Headers.Add(new PasswordSoapHeader { EncodedPassword = creds.Password });
                    }
                    break;
                case SoapMessageStage.AfterSerialize:
                    break;
                case SoapMessageStage.BeforeDeserialize:
                    break;
                case SoapMessageStage.AfterDeserialize:
                    break;
            }
        }

        public bool IsEnabledForUrl(string url)
        {
            bool isEnabled;
            EnabledServices.TryGetValue(url, out isEnabled);
            return isEnabled;
        }
    }
}

Step 3 – Add the SoapExtension to the .config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <!-- Other stuff -->

  <system.web>
    <webServices>
      <soapExtensionTypes>
        <add type="ConnectToFlexeraExample.Extensions.SoapHeaderInjectionExtension, ConnectToFlexeraExample" priority="1" group="High" />
        <add type="ConnectToFlexeraExample.Extensions.SoapLoggerExtension, ConnectToFlexeraExample" priority="2" group="Low" />
      </soapExtensionTypes>
    </webServices>
  </system.web>
</configuration>

Step 4 – Update SetNetworkCredentials extension method

We now want this method to accept either SOAP or Basic auth. We are going to key off of PreAuthenticate.

    public static class WebClientProtocolExtensions
    {
        public static void SetNetworkCredentials(this WebClientProtocol client, string username, string password, string customUrl = null, bool preAuthenticate = true)
        {
            client.PreAuthenticate = preAuthenticate;
            client.Url = string.IsNullOrWhiteSpace(customUrl) ? client.Url : customUrl;
            var netCredential = new NetworkCredential(username, password);
            if (preAuthenticate)
            {
                ICredentials credentials = netCredential.GetCredential(new Uri(client.Url), "Basic");
                client.Credentials = credentials;
            }
            else
            {
                SoapHeaderInjectionExtension.EnabledServices[client.Url] = true;
                SoapHeaderInjectionExtension.UserAndPassword[client.Url] = netCredential;
            }
        }
    }

Step 5 – Use the new service clients with SOAP

    private static void TestAuthenticationService(string user, string pass)
    {
        // Basic Auth Method
        // authenticationServiceClient.SetNetworkCredentials(user, pass);
        // Soap Auth Method
        authenticationServiceClient.SetNetworkCredentials(user, pass, null, false);
        var userInputType = new AuthenticateUserInputType
        {
            userName = user,
            password = pass,
            domainName = "FLEXnet"
        };
        var result = authenticationServiceClient.authenticateUser(userInputType);
        Logger.Info(result.Success);
    }

And you are autenticating to Flexera SOAP-based java web services using C#!

Leave a Reply

How to post code in comments?